﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.ComponentModel.Composition;
using Caliburn.Micro;
using ISC.MapDotNetServer.Common.Maps;
using ISC.RIM.Silverlight;
using ISC.RIM.Silverlight.Requestor;
using OI.Controls;
using OI.DataModel;
using OI.Events;
using OI.Framework;
using OI.Views;
using Map = ISC.RIM.Silverlight.Map;

namespace OI.ViewModels
{
    [Export(typeof(DatasetDetailsMapViewModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class DatasetDetailsMapViewModel : Screen
    {
        #region DATA
        private readonly IEventAggregator Events;
        public bool IsMapInitialized {get; private set;}
        public Map RIMMap {get; private set;}
        public TileLayer BaseMapTileLayer { get; private set; }
        public TileLayer MDNTileLayer { get; private set; }
        public MashupMapDefinition MashupMapDefinition { get; private set; }
        public Layer LayerShown { get; private set; }

        public OIDataset DatasetDetail
        {
            get
            {
                return _DatasetDetail;
            }
            set
            {
                var old = _DatasetDetail;
                _DatasetDetail = value;

                // if we are switching to a new dataset
                if (value != old)
                {
                    MDNMapRequest = NewDescriptor();
                }
            }
        }
        private OIDataset _DatasetDetail;


        // this is the descriptor on the MDN UX TileLayer on the map
        // it defines all of the spatial layers displayed
        public MapRequest MDNMapRequest
        {
            get
            {
                if (_MDNMapRequest == null &&
                    MDNTileLayer != null &&
                    MDNTileLayer.Descriptor is MapRequest)
                {
                    _MDNMapRequest = (MapRequest)(MDNTileLayer.Descriptor);
                }
                return _MDNMapRequest;
            }
            private set
            {
                _MDNMapRequest = value;

                if (MDNTileLayer != null)
                {
                    MDNTileLayer.Descriptor = _MDNMapRequest;
                }
            }
        }
        private MapRequest _MDNMapRequest;
        #endregion

       
        [ImportingConstructor]
        public DatasetDetailsMapViewModel(IEventAggregator events, MashupMapDefinition mmd)
        {
            Events = events;
            events.Subscribe(this);
            MashupMapDefinition = mmd;
        }


        // early in the lifecycle provides opportunity to tap into the map initialization process
        // we need to dynamically add an MDNS map TileLayer based on the map ID defined in DatasetDetail
        protected override void OnViewAttached(object view, object context)
        {
            base.OnViewAttached(view, context);

            if (!IsMapInitialized)
            {
                // get Map from view (layout is not in place yet)
                RIMMap = ((DatasetDetailsMapView)view).myMap;
                RIMMap.InitializeMVCComplete += InitializeMVCComplete;
                RIMMap.PostInitializePosition += PostInitializePosition;
                RIMMap.ExceptionEvent += OnError;

                // set the MDN services endpoint
                RIMMap.MDNSAddress = Configuration.GetBaseURI() + Configuration.MDNSEndpoint;

                // Bing
                BaseMapTileLayer = ((DatasetDetailsMapView)view).BaseMapTileLayer;
                var tr = (BingMapsTileRequestor)(BaseMapTileLayer.TileRequestor);
                tr.EndpointAddress = "http://ecn.t{1}.tiles.virtualearth.net/tiles/{0}{2}.{3}?g={4}&mkt=en-us&lbl=l1&stl=h&shading=hill&n=z";
                tr.Generation = Configuration.BingMapsGeneration;

                // MDN TileLayer
                MDNTileLayer = ((DatasetDetailsMapView)view).MDNTileLayer;
                MDNMapRequest = NewDescriptor();
            }
            else
            {
                SetZoomLevelForSampleData();
            }

            // we now have a fully configured RIM map displayed
            // send out notifications
            // RIM Map
            Events.Publish(new DisplayRIMMap
            {
                Map = RIMMap,
                BaseTileLayer = BaseMapTileLayer,
                MDNTileLayer = MDNTileLayer
            });

            // UX Map definition 
            Events.Publish(new SelectMap
            {
                Map = MDNMapRequest.Map
            });

            // enable drill-down
            Events.Publish(new EnableDrillDown
            {
                Enable = true
            });
        }


        // shut off drill-down when leaving
        protected override void OnDeactivate(bool close)
        {
            base.OnDeactivate(close);
            Events.Publish(new EnableDrillDown
            {
                Enable = false
            });
        }


        // called by RIM when the map MVC model is initialized
        public void InitializeMVCComplete(object sender, EventArgs e)
        {
            RIMMap.MapController.ForwardOnlyUnhandledMouseGestures = true;
            RIMMap.MapView.EnableMouseHoverEvents = MDNMapRequest.Map.AreMouseHoverEventsEnabled();

            IsMapInitialized = true;
        }


        // called by RIM when map position is initialized
        public void PostInitializePosition(object sender, EventArgs e)
        {
            SetZoomLevelForSampleData();
        }


        // RIM exceptions route here, forward onto shell
        public void OnError(object sender, MapControlExceptionEventArgs arg)
        {
            IoC.Get<IShell>().HandleException(arg.Exception, "MDNUX map error:");
        }


        // mouse hover on the map
        public void MouseHover(PointPairEventArgs parm)
        {
            Events.Publish(new MouseHoverOverMap
            {
                Location = parm
            });
        }


        // mouse left click on map
        public void MouseLeftSingleClickPosition(PointPairEventArgs parm)
        {
            Events.Publish(new MouseLeftClickOnMap
            {
                Location = parm
            });
        }


        private MapRequest NewDescriptor()
        {
            // setup MapRequest descriptor
            var mashupMap = MashupMapDefinition.Copy();
            LayerShown = mashupMap.GetLayerByID(DatasetDetail.LayerID);
            if (LayerShown != null)
            {
                LayerShown.Visible = true;
                mashupMap.Layers.Clear();
                mashupMap.Layers.Add(LayerShown);

                // set the descriptor to the new mashup
                var mr = new MapRequest { ImageType = MapRequest.SupportedImageTypes.PNG8, Map = mashupMap };
                return mr;
            }

            IoC.Get<IShell>().HandleException(new Exception("LayerID was not found"), DatasetDetail.LayerID);
            return null;
        }


        private void SetZoomLevelForSampleData()
        {
            if (RIMMap != null && LayerShown != null)
            {
                // update position to make sure we are at a zoom level where data is displayed
                double ms = LayerShown.GetMaxScale();
                if (ms > 0)
                {
                    double curScale = RIMMap.MapView.GetProjectionMapScale();
                    double factor = curScale / (0.5 * ms);
                    double zlInc = Math.Log(factor, 2.0);
                    RIMMap.ZoomLevel += zlInc;
                }
            }
        }


        #region NAVIGATOR
        // Handles ModeChanged event of the navigator control for selecting base layer type.
        public void NavigatorModeChanged(NavModeEventArgs e)
        {
            if (BaseMapTileLayer != null)
            {
                BaseMapTileLayer.Descriptor = e.Mode;
            }
        }


        // Handles the Zoomed event of the navigator control.
        public void NavigatorZoomed(NavZoomEventArgs e)
        {
            if (RIMMap.IsMVCInitialized)
            {
                double z = RIMMap.ZoomLevel + e.ZoomChange;
                if (z > 20) { z = 20; } else if (z < 1) { z = 1; }
                if (z != RIMMap.ZoomLevel)
                {
                    RIMMap.ZoomLevelAnimate = z;
                }
            }
        }


        // Handles the Panned event of the navigator control.
        public void NavigatorPanned(NavPanEventArgs e)
        {
            if (RIMMap.IsMVCInitialized)
            {
                Envelope curExt = RIMMap.MapView.GetMapExtents();
                double magX = curExt.MagX / 10.0;
                double magY = curExt.MagY / 10.0;
                ISC.MapDotNetServer.Common.Point newCenter = curExt.Center.ToMDNSPoint().Offset(magX * e.PanVector.X, magY * e.PanVector.Y);
                Execute.OnUIThread(() => RIMMap.MapCenterAnimate = newCenter.ToSysWinPoint());
            }
        }

        #endregion
    }
}
